#ifndef BEZIER_SURFACE_H
#define BEZIER_SURFACE_H

#include "Vector3.h"
#include "MathFunctions.h"
#include <vector>


class BezierSurface
{
private:
	std::vector<Vector3<float>> curve1ControlPoints;					// The Control Points that calculate the Curve
	std::vector<Vector3<float>> curve2ControlPoints;					// The Control Points that calculate the Curve
	std::vector<std::vector<Vector3<float>>> pointsOnTheCurve;			// Points that are a part of the curve
	static const unsigned __int8 NUMBER_OF_TIMESTEPS = 30;				// Number of time steps throughout the curve

public:

	BezierSurface(void)
	{

	}

	// Constructor
	BezierSurface(std::vector<Vector3<float>> points1, std::vector<Vector3<float>> points2)
	{
		curve1ControlPoints = points1;
		curve2ControlPoints = points2;
		CreatePoints();
	}

	// Deconstructor
	~BezierSurface(void)
	{

	}

	// Create points on the Curve
	void CreatePoints()
	{

		unsigned __int8 CONTROL_POINT1_COUNT = curve1ControlPoints.size();
		unsigned __int8 CONTROL_POINT2_COUNT = curve2ControlPoints.size();
		unsigned __int8 N1 = CONTROL_POINT1_COUNT - 1;
		unsigned __int8 N2 = CONTROL_POINT2_COUNT - 1;

		unsigned __int8 i,j, ii, jj;			

		// Time Step through the Curve
		const float timeStepIncrement = 1.0f / NUMBER_OF_TIMESTEPS;
		float currentTimeStep1 = 0;	
		float currentTimeStep2 = 0;	

		// Factorials 
		float n1Factorial = Factorial(N1);
		float n2Factorial = Factorial(N2);

		Vector3<float> tempVector;
		Vector3<float> control1CalcedVector;
		std::vector<Vector3<float>> tempVectorList;

		for(i = 0; i < NUMBER_OF_TIMESTEPS + 1; i++)
		{
			tempVector = Vector3<float>(0,0,0);
			control1CalcedVector = Vector3<float>(0,0,0);

			for(j = 0; j < CONTROL_POINT1_COUNT; j++)
			{					// n! / !i * (n - i)!
				control1CalcedVector += curve1ControlPoints[j] * ((n1Factorial / (Factorial(j) * Factorial(N1 - j))) * 
					pow((1 - currentTimeStep1),  N1 - j) * pow(currentTimeStep1, j));
			}			

			for(ii = 0; ii < NUMBER_OF_TIMESTEPS + 1; ii++)
			{
				tempVector = control1CalcedVector;
				for(jj = 0; jj < CONTROL_POINT2_COUNT; jj++)
				{					// n! / !i * (n - i)!
					tempVector += curve2ControlPoints[jj] * ((n2Factorial / (Factorial(jj) * Factorial(N2 - jj))) * 
						pow((1 - currentTimeStep2),  N2 - jj) * pow(currentTimeStep2, jj));
				}

				tempVectorList.push_back(tempVector);
				currentTimeStep2 += timeStepIncrement;
			}

			pointsOnTheCurve.push_back(tempVectorList);
			tempVectorList.clear();
			currentTimeStep2 = 0;
			currentTimeStep1 += timeStepIncrement;
		}
	}

	// Draws the Curve
	void Draw(void) 
	{

		glBegin(GL_TRIANGLES); 
		glColor3f(.3,.5,.7);
		for(unsigned __int8 i = 0; i < pointsOnTheCurve.size() - 1; i++)
		{
			for(unsigned __int8 j = 0; j < pointsOnTheCurve[i].size() - 1; j++)
			{
				glVertex3f(pointsOnTheCurve[i][j].X(), pointsOnTheCurve[i][j].Y(), pointsOnTheCurve[i][j].Z());
				glVertex3f(pointsOnTheCurve[i][j + 1].X(), pointsOnTheCurve[i][j + 1].Y(), pointsOnTheCurve[i][j + 1].Z());
				glVertex3f(pointsOnTheCurve[i + 1][j + 1].X(), pointsOnTheCurve[i + 1][j + 1].Y(), pointsOnTheCurve[i + 1][j + 1].Z());
				

				glVertex3f(pointsOnTheCurve[i + 1][j + 1].X(), pointsOnTheCurve[i + 1][j + 1].Y(), pointsOnTheCurve[i + 1][j + 1].Z());
				glVertex3f(pointsOnTheCurve[i + 1][j].X(), pointsOnTheCurve[i + 1][j].Y(), pointsOnTheCurve[i + 1][j].Z());
				glVertex3f(pointsOnTheCurve[i][j].X(), pointsOnTheCurve[i][j].Y(), pointsOnTheCurve[i][j].Z());



				glVertex3f(pointsOnTheCurve[i + 1][j + 1].X(), pointsOnTheCurve[i + 1][j + 1].Y(), pointsOnTheCurve[i + 1][j + 1].Z());
				glVertex3f(pointsOnTheCurve[i][j + 1].X(), pointsOnTheCurve[i][j + 1].Y(), pointsOnTheCurve[i][j + 1].Z());
				glVertex3f(pointsOnTheCurve[i][j].X(), pointsOnTheCurve[i][j].Y(), pointsOnTheCurve[i][j].Z());

				glVertex3f(pointsOnTheCurve[i][j].X(), pointsOnTheCurve[i][j].Y(), pointsOnTheCurve[i][j].Z());
				glVertex3f(pointsOnTheCurve[i + 1][j].X(), pointsOnTheCurve[i + 1][j].Y(), pointsOnTheCurve[i + 1][j].Z());
				glVertex3f(pointsOnTheCurve[i + 1][j + 1].X(), pointsOnTheCurve[i + 1][j + 1].Y(), pointsOnTheCurve[i + 1][j + 1].Z());
			}
		}
		glEnd();
	}
};

#endif

